package cn.factorybox.item.service.impl;

import cn.factorybox.common.utils.UUIDUtil;
import cn.factorybox.entity.UserInfo;
import cn.factorybox.item.config.CodeCache;
import cn.factorybox.item.config.JwtProperties;
import cn.factorybox.item.mapper.FactoryDeviceDataMapper;
import cn.factorybox.item.mapper.FactoryDeviceMapper;
import cn.factorybox.item.mapper.RoomMapper;
import cn.factorybox.item.service.FactoryDeviceService;
import cn.factorybox.model.entity.FactoryDevice;
import cn.factorybox.model.entity.FactoryDeviceData;
import cn.factorybox.model.entity.FactoryJudgeValue;
import cn.factorybox.model.entity.Room;
import cn.factorybox.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;

/**
 * @author flypig
 * @version 1.0
 * @date 2020-09-11 11:25
 */

@Service
public class FactoryDeviceServiceImpl implements FactoryDeviceService {

    @Autowired
    private FactoryDeviceMapper factoryDeviceMapper;

    @Autowired
    private FactoryDeviceDataMapper deviceDataMapper;

    @Autowired
    private RoomMapper roomMapper;

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void save(FactoryDevice factoryDevice) {
        List<String> values = (List<String>) redisTemplate.opsForList().range("roomId:" + factoryDevice.getRoomId(), 0, -1);
        int number = Integer.parseInt(factoryDevice.getNumber());   // 房间编号，用于计算拼接设备编号
        // System.out.println("number:" + number);
        if (values.isEmpty()) {
            // 如果为空，则表示该房间还未创建设备，直接根据number生成设备号
            factoryDevice.setValue((number < 10 ? "0" : "") + (number * 10 + 1));
        } else {
            for (int i = 0; i < values.size(); i++) {
                String deviceNum = (number < 10 ? "0" : "") + (number * 10 + i + 1);
                if (!deviceNum.equals(values.get(i))) {
                    factoryDevice.setValue(deviceNum);
                    break;
                }
                if (i == (values.size() - 1)) {
                    factoryDevice.setValue((number < 10 ? "0" : "") + (number * 10 + i + 2));
                }
            }
        }
        factoryDevice.setId(UUIDUtil.uuid());
        factoryDevice.setStar(2);
        factoryDevice.setCreateTime(new Date());
        factoryDeviceMapper.save(factoryDevice);
        refreshDeviceCache(); // 刷新redis缓存
    }

    @Override
    public int update(FactoryDevice factoryDevice) {
        return factoryDeviceMapper.update(factoryDevice);
    }

    @Override
    public void delete(String id) {
        // 删除设备的同时，删除该设备采集的数据，通过设置外键实现
//        deviceDataMapper.deleteDataByDeviceId(id);
        factoryDeviceMapper.delete(id);
        refreshDeviceCache();
    }

    @Override
    public Map<String, Object> selectList(Integer number) {
        Map<String, Object> data = new HashMap<>();
        List<Map<String, Object>> results = factoryDeviceMapper.selectList(number);
        data.put("data", results);
        return data;
    }

    @Override
    public Map<String, Object> selectAllDevices() {
        //先查询所有的房间，再逐一遍历设备
        List<Map<String, Object>> rooms = roomMapper.selectAllRooms();
        for (Map<String, Object> room : rooms) {
            //这里不能用强转，参考：https://blog.csdn.net/acwlb/article/details/81975117
            Integer number = Integer.parseInt(room.get("number").toString());
            List<Map<String, Object>> device = factoryDeviceMapper.selectByNumber(number);
            room.put("device", device);
        }
        Map<String, Object> data = new HashMap<>();
        data.put("data", rooms);
        return data;
    }

    @Override
    public Map<String, Object> getDistributeMessage() {
        Map<String, Object> map = new HashMap<>();
        Map<String, Object> data = new HashMap<>();
        Double room = factoryDeviceMapper.getSumRoom();
        Double over = factoryDeviceMapper.getOver();
        Integer deviceNum = factoryDeviceMapper.getDeviceNum();
        Integer[] overRate = new Integer[2];
        overRate[0] = (int) ((over / room) * 100);
        overRate[1] = overRate[0] - 17;     //据黄老板说这么剪，他的图显示比较好看，按大佬说得剪就完事了
        data.put("room", room);
        data.put("over", over);
        data.put("device", deviceNum);
        data.put("overRate", overRate);
        map.put("data", data);
        return map;
    }

    @Override
    public List<FactoryDevice> selectListByRoomId(String roomId) {
        return factoryDeviceMapper.selectListByRoomId(roomId);
    }

    @Override
    public List<FactoryDevice> selectAllDevicesOfVersion3(HttpServletRequest request) throws Exception {
        String token = request.getHeader("token");
        UserInfo userInfo = JwtUtils.getInfoFromToken(token, this.jwtProperties.getKey());
        String companyId = userInfo.getCompanyId();
        return factoryDeviceMapper.selectAllDevicesOfVersion3(companyId);
    }

    @Override
    public Map<String, Object> getSingleDeviceData(String id) {
        long startTime = System.currentTimeMillis();
        Map<String, Object> device = factoryDeviceMapper.selectDeviceById(id);
        String iCount = (String) device.get("type"); // 设备类型，5或7
        Map<String, Object> count = new HashMap<>();
        // 串行
        count.put("all", factoryDeviceMapper.countAll(id));
        count.put("normal", factoryDeviceMapper.countNormal(id));
        count.put("warning", factoryDeviceMapper.countWarning(id));
        device.put("count", count);
        device.put("lastest", lastestDeviceDataProcessing(deviceDataMapper.selectLastestData(id)));
        //前31条数据的各项数组
        device.put("list", dataListProcessing(deviceDataMapper.selectThirtyOneDataList(id), iCount));
        long endTime = System.currentTimeMillis();
//        System.out.println("运行时间：" + (endTime - startTime));
        return device;
    }

    @Override
    public FactoryDevice getFactoryDevice(String id) {
        return factoryDeviceMapper.getFactoryDeviceById(id);
    }

    @Override
    public Map<String, Object> getSingleDeviceLatestData(String id) {
        Map<String, Object> data = new HashMap<>();
        Map<String, Object> count = new HashMap<>();
        // 查找id对应的设备
        Map<String, Object> device = factoryDeviceMapper.selectDeviceById(id);
        String iCount = (String) device.get("type"); // 设备类型，5或7
        // 查找该设备最新一条数据
        FactoryDeviceData lastest = deviceDataMapper.selectLastestDataOfDevice(id);
        data.put("lastest", lastestProcessing(lastest, iCount));
        data.put("mult", multProcessing(lastest, iCount));
        data.put("list", dataListProcessing(deviceDataMapper.selectThirtyOneDataList(id), iCount));
        // 正常数据量，[今日温度正常的数据量,湿度...,CO2...,Tvoc...,压强...,PM2.5...,臭氧...,风速...,]，注意：当设备是五项时臭氧和风速的去掉
        // 先查询总数据量
        Integer allCount = deviceDataMapper.countAllOfSingleDevice(id);
        List<Integer> proportion = proportionProcessing(allCount, iCount, deviceDataMapper.countWarningOfEachItem(id));
        data.put("proportion", proportion);
        count.put("all", allCount);
        count.put("normal", deviceDataMapper.countNormalOfSingleDevice(id));
        count.put("warning", deviceDataMapper.countWarningOfSingleDevice(id));
        data.put("count", count);

        return data;
    }

    @Override
    public List<Integer> countAllBeforeAweek(String id) {
        return deviceDataMapper.countAllBeforeAweekByDeviceId(id);
    }

    @Override
    public Map<String, Object> selectAllDevicesOfCompany(String companyId) {
        Map<String, Object> map = new HashMap<>();
        map.put("valueLis", factoryDeviceMapper.selectValueList(companyId));
        map.put("idLis", factoryDeviceMapper.selectIdList(companyId));
        return map;
    }

    /**
     * 获取单个设备的数据总览--对最新数据数据的处理
     *
     * @param deviceData
     * @return
     */
    private static Map<String, Object> lastestDeviceDataProcessing(FactoryDeviceData deviceData) {
        Map<String, Object> lastest = new HashMap<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        DecimalFormat df = new DecimalFormat("0.0");    // 格式化数据
        List<FactoryJudgeValue> judgeValues = CodeCache.thresholdList;
        List<Table> table = new ArrayList<>();
        List<Mult> mult = new ArrayList<>();
        lastest.put("datetime", " / : : ");
        String[] titles = {"温度", "湿度", "二氧化碳", "气体浓度", "pm2.5"};
        String[] units = {"℃", "%", "ppm", "ppb", "hPa"};
        for (int i = 0; i < 5; i++) {
            Table item = new Table();
            Mult element = new Mult();
            item.setTitle(titles[i]);
            item.setValue(0.0);
            item.setUnit(units[i]);
            item.setState("normal");
            element.setName(titles[i]);
            element.setValue(0.0);
            table.add(item);
            mult.add(element);
        }
        // 通过判断时间字段是否有值，有值则表示有数据，避免空指针异常
        if (deviceData != null) {
            lastest.put("datetime", sdf.format(deviceData.getfCreateTime()));
            table.get(0).setValue(deviceData.getfTemperature());
            table.get(1).setValue(deviceData.getfHumidity());
            table.get(2).setValue(deviceData.getfCarbonDioxide());
            table.get(3).setValue(deviceData.getfTvoc());
            table.get(4).setValue(deviceData.getfPm());
            if (deviceData.getfTemperature() < judgeValues.get(0).getMin() || deviceData.getfTemperature() > judgeValues.get(0).getMax()) {
                table.get(0).setState("warning");
                if (deviceData.getfTemperature() < judgeValues.get(0).getMin()) {
                    mult.get(0).setValue(Double.valueOf(df.format(judgeValues.get(0).getMin() - deviceData.getfTemperature())));
                } else if (deviceData.getfTemperature() > judgeValues.get(0).getMax()) {
                    mult.get(0).setValue(Double.valueOf(df.format(deviceData.getfTemperature() - judgeValues.get(0).getMax())));
                }
            }
            if (deviceData.getfHumidity() < judgeValues.get(1).getMin() || deviceData.getfHumidity() > judgeValues.get(1).getMax()) {
                table.get(1).setState("warning");
                if (deviceData.getfHumidity() < judgeValues.get(1).getMin()) {
                    mult.get(1).setValue(Double.valueOf(df.format(judgeValues.get(1).getMin() - deviceData.getfHumidity())));
                } else if (deviceData.getfHumidity() > judgeValues.get(1).getMax()) {
                    mult.get(1).setValue(Double.valueOf(df.format(deviceData.getfHumidity() - judgeValues.get(1).getMax())));
                }
            }
            if (deviceData.getfCarbonDioxide() > judgeValues.get(2).getMax()) {
                table.get(2).setState("warning");
                mult.get(2).setValue(Double.valueOf(df.format(deviceData.getfCarbonDioxide() - judgeValues.get(2).getMax())));
            }
            if (deviceData.getfTvoc() > judgeValues.get(3).getMax()) {
                table.get(3).setState("warning");
                mult.get(3).setValue(Double.valueOf(df.format(deviceData.getfTvoc() - judgeValues.get(3).getMax())));
            }
            if (deviceData.getfPm() > judgeValues.get(7).getMax()) {
                table.get(4).setState("warning");
                mult.get(4).setValue(Double.valueOf(df.format(deviceData.getfPm() - judgeValues.get(7).getMax())));
            }
        }
        lastest.put("table", table);
        lastest.put("mult", mult);
        return lastest;
    }

    /**
     * 获取单个设备的数据总览--处理前31条数据
     *
     * @param list
     * @return
     */
    private static Map<String, Object> dataListProcessing(List<FactoryDeviceData> list, String iCount) {
        int len = list.size();
        boolean flag = false;   // 是否有数据标识
        Map<String, Object> result = new HashMap<>();
        String[] names = {"temp", "hum", "co2", "press", "tvoc", "pm25", "wind", "o3"};
        List<Double[]> dataList = new ArrayList<>();
        for (int i = 0; i < 8; i++) {
            dataList.add(new Double[31]);
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());   // 设置时间为当前时间
        for (int i = 0, j = 30; j >= 0; j--) {
            Date date = calendar.getTime();
            if (i < len && sdf.format(date).equals(sdf.format(list.get(i).getfCreateTime()))) {
                flag = true;
            }
            dataList.get(0)[j] = flag ? list.get(i).getfTemperature() : 0;     // 温度
            dataList.get(1)[j] = flag ? list.get(i).getfHumidity() : 0;     // 湿度
            dataList.get(2)[j] = flag ? list.get(i).getfCarbonDioxide() : 0;     // co2
            dataList.get(3)[j] = flag ? list.get(i).getfPressure() : 0;     // press
            dataList.get(4)[j] = flag ? list.get(i).getfTvoc() : 0;     // tvoc
            dataList.get(5)[j] = flag ? list.get(i).getfPm() : 0;     // pm25
            dataList.get(6)[j] = flag ? list.get(i).getfWind() : 0;     // wind
            dataList.get(7)[j] = flag ? list.get(i).getfO3() : 0;     // o3
            if (flag && i < len - 1) {
                i++;
            }
            flag = false;
            calendar.add(Calendar.MINUTE, -1);
        }
        int num = 0;
        if (iCount.equals("7")) {
            num = 8;
        } else if (iCount.equals("5")) {
            num = 6;
        }
        for (int i = 0; i < num; i++) {
            result.put(names[i], dataList.get(i));
        }
        return result;
    }

    /**
     * 获取设备最新展示数据--处理对应设备最新一条数据
     *
     * @param factoryDeviceData
     * @param iCount
     * @return
     */
    private static Object lastestProcessing(FactoryDeviceData factoryDeviceData, String iCount) {
        if (factoryDeviceData == null) {
            Map<String, Object> map = new HashMap<>();
            map.put("time", "--:--:--");
            String[] names = {"temp", "hum", "co2", "press", "tvoc", "pm25", "wind", "o3"};
            int num = 0;
            if (iCount.equals("7")) {
                num = 8;
            } else if (iCount.equals("5")) {
                num = 6;
            }
            for (int i = 0; i < num; i++) {
                map.put(names[i], 0);
            }
            return map;
        }
        return factoryDeviceData;
    }

    /**
     * 获取设备最新展示数据--处理最新一条数据的各项阈值差
     *
     * @param deviceData
     * @param iCount
     * @return
     */
    private static List<Mult> multProcessing(FactoryDeviceData deviceData, String iCount) {
        List<Mult> multList = new ArrayList<>();
        DecimalFormat df = new DecimalFormat("0.0");    // 格式化数据
        List<FactoryJudgeValue> judgeValues = CodeCache.thresholdList;
        String[] names = {"温度", "湿度", "二氧化碳", "气体浓度", "PM2.5", "大气压强", "臭氧", "风速"};
        int num = 0;
        if (iCount.equals("7")) {
            num = 8;
        } else if (iCount.equals("5")) {
            num = 6;
        }
        for (int i = 0; i < num; i++) {
            Mult item = new Mult();
            item.setValue(0.0);
            item.setName(names[i] + "(0)");
            multList.add(item);
        }
        if (deviceData != null) {
            // 温度
            if (deviceData.getfTemperature() < judgeValues.get(0).getMin() || deviceData.getfTemperature() > judgeValues.get(0).getMax()) {
                Double difference = 0.0;
                if (deviceData.getfTemperature() < judgeValues.get(0).getMin()) {
                    difference = Double.valueOf(df.format(judgeValues.get(0).getMin() - deviceData.getfTemperature()));
                    multList.get(0).setValue(difference);
                } else if (deviceData.getfTemperature() > judgeValues.get(0).getMax()) {
                    difference = Double.valueOf(df.format(deviceData.getfTemperature() - judgeValues.get(0).getMax()));
                    multList.get(0).setValue(difference);
                }
                multList.get(0).setValue(difference);
                multList.get(0).setName("温度(" + difference + ")");
            }
            // 湿度
            if (deviceData.getfHumidity() < judgeValues.get(1).getMin() || deviceData.getfHumidity() > judgeValues.get(1).getMax()) {
                Double difference = 0.0;
                if (deviceData.getfHumidity() < judgeValues.get(1).getMin()) {
                    difference = Double.valueOf(df.format(judgeValues.get(1).getMin() - deviceData.getfHumidity()));
                    multList.get(1).setValue(difference);
                } else if (deviceData.getfHumidity() > judgeValues.get(1).getMax()) {
                    difference = Double.valueOf(df.format(deviceData.getfHumidity() - judgeValues.get(1).getMax()));
                    multList.get(1).setValue(difference);
                }
                multList.get(1).setValue(difference);
                multList.get(1).setName("湿度(" + difference + ")");
            }
            // 二氧化碳
            if (deviceData.getfCarbonDioxide() > judgeValues.get(2).getMax()) {
                Double difference = Double.valueOf(df.format(deviceData.getfCarbonDioxide() - judgeValues.get(2).getMax()));
                multList.get(2).setValue(difference);
                multList.get(2).setName("二氧化碳(" + difference + ")");
            }
            // 气体浓度
            if (deviceData.getfTvoc() > judgeValues.get(3).getMax()) {
                Double difference = Double.valueOf(df.format(deviceData.getfTvoc() - judgeValues.get(3).getMax()));
                multList.get(3).setValue(difference);
                multList.get(3).setName("气体浓度(" + difference + ")");
            }
            // pm2.5
            if (deviceData.getfPm() > judgeValues.get(7).getMax()) {
                Double difference = Double.valueOf(df.format(deviceData.getfPm() - judgeValues.get(7).getMax()));
                multList.get(4).setValue(difference);
                multList.get(4).setName("PM2.5(" + difference + ")");
            }
            // 大气气压
            if (deviceData.getfPressure() > judgeValues.get(4).getMax()) {
                Double difference = Double.valueOf(df.format(deviceData.getfPressure() - judgeValues.get(4).getMax()));
                multList.get(5).setValue(difference);
                multList.get(5).setName("大气压强(" + difference + ")");
            }
            // 臭氧
            if (iCount.equals("7") && deviceData.getfO3() > judgeValues.get(5).getMax()) {
                Double difference = Double.valueOf(df.format(deviceData.getfO3() - judgeValues.get(5).getMax()));
                multList.get(6).setValue(difference);
                multList.get(6).setName("臭氧(" + difference + ")");
            }
            // 风速
            if (iCount.equals("7") && deviceData.getfWind() > judgeValues.get(6).getMax()) {
                Double difference = Double.valueOf(df.format(deviceData.getfWind() - judgeValues.get(6).getMax()));
                multList.get(7).setValue(difference);
                multList.get(7).setName("风速(" + difference + ")");
            }
            return multList;
        }
        return multList;
    }

    /**
     * 获取设备最新展示数据--处理今日个项正常数据量
     *
     * @param allCount  今日总数据量
     * @param iCount    设备类型，5或7
     * @param countList 各项异常数据量
     * @return
     */
    private static List<Integer> proportionProcessing(Integer allCount, String iCount, List<Integer> countList) {
        List<Integer> proportion = new ArrayList<>();
        proportion.add(allCount - countList.get(0));
        proportion.add(allCount - countList.get(1));
        proportion.add(allCount - countList.get(2));
        proportion.add(allCount - countList.get(3));
        proportion.add(allCount - countList.get(4));
        proportion.add(allCount - countList.get(5));
        if (iCount.equals("7")) {
            proportion.add(allCount - countList.get(6));
            proportion.add(allCount - countList.get(7));
        }
        return proportion;
    }

    /**
     * 进行设备数据的缓存，将所有设备号缓存到redis
     */
    private void refreshDeviceCache() {
        // 进行设备数据的缓存，将所有设备号缓存到redis
        List<FactoryDevice> daviceList = factoryDeviceMapper.selectAllDevicesForRedisCache();
        // 先清空redis
        Set<String> keys = redisTemplate.keys("roomId:*");
        redisTemplate.delete(keys);     // 需要重新定义key的序列化方式，否则无法删除
        daviceList.forEach(factoryDevice -> {
            redisTemplate.opsForList().rightPush("roomId:" + factoryDevice.getRoomId(), factoryDevice.getValue());
        });
    }
}

class Table {
    String title;
    Double value;
    String unit;
    String state;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Double getValue() {
        return value;
    }

    public void setValue(Double value) {
        this.value = value;
    }

    public String getUnit() {
        return unit;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

class Mult {
    Double value;
    String name;

    public Double getValue() {
        return value;
    }

    public void setValue(Double value) {
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}